package muhnes.sizoo.controlador;

import java.io.Serializable;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.model.SelectItem;
import muhnes.sizoo.consulta.Campo;
import muhnes.sizoo.consulta.CampoFormulario;
import muhnes.sizoo.consulta.ConstructorFormulario;
import muhnes.sizoo.consulta.ConsultaAvanzada;
import muhnes.sizoo.consulta.Filtro;
import muhnes.sizoo.consulta.Fuente;
import muhnes.sizoo.consulta.Ordenador;
import muhnes.sizoo.consulta.TipoComparacion;
import muhnes.sizoo.consulta.TipoOrdenacion;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.DocumentException;
import org.primefaces.event.FlowEvent;
import org.primefaces.extensions.model.dynaform.AbstractDynaFormElement;
import org.primefaces.extensions.model.dynaform.DynaFormControl;
import org.primefaces.extensions.model.dynaform.DynaFormModel;
import org.primefaces.extensions.model.dynaform.DynaFormRow;

/**
 * Clase bean utilizado para realizar las consultas dinamicas dependiendo 
 * de lo que el usuario necesita
 */

@ManagedBean(name = "consultaBean")
@ViewScoped
public class ConsultaBean implements Serializable {
    
    private String consultaSQL;
    private ConsultaAvanzada consulta;
    
    private DynaFormModel modeloSelectores;
    private DynaFormModel modeloFiltros;
    private DynaFormModel modeloOrdenadores;
    
    private String fuenteFiltro;
    private String campoFiltro;
    private List<SelectItem> itemsFuenteFiltro;
    private List<SelectItem> itemsCampoFiltro;
    
    private String fuenteOrdenador;
    private String campoOrdenador;
    private List<SelectItem> itemsFuenteOrdenador;
    private List<SelectItem> itemsCampoOrdenador;
    
    /**
     * Maneja los distintos elementos que utilizan para filtras las consultas
     * dependiendo de lo que el usuario eligio para consultar
     */
    public ConsultaBean() {
        try {
            modeloSelectores = new DynaFormModel();  
            modeloFiltros = new DynaFormModel();  
            modeloOrdenadores = new DynaFormModel();  
            
            itemsFuenteFiltro = new ArrayList<SelectItem>();
            itemsCampoFiltro = new ArrayList<SelectItem>();
            itemsFuenteOrdenador = new ArrayList<SelectItem>();
            itemsCampoOrdenador = new ArrayList<SelectItem>();
            
            FacesContext contexto = FacesContext.getCurrentInstance();
            String rutaRelativa = "/WEB-INF/consulta/consulta-avanzada.xml";
            String ruta = contexto.getExternalContext().getRealPath(rutaRelativa);
            consulta = ConsultaAvanzada.cargarDefinicion(ruta);
            
            generarSelectores();

            
        } catch (DocumentException ex) {
            Logger.getLogger(ConsultaBean.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SQLException ex) {
            Logger.getLogger(ConsultaBean.class.getName()).log(Level.SEVERE, null, ex);
        } catch (Exception ex) {
            Logger.getLogger(ConsultaBean.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    public String getConsultaSQL() {
        if( (consultaSQL == null || consultaSQL.isEmpty())
            && ( !consulta.getSelectores().isEmpty() ) ) {
            consultaSQL = consulta.crearConsultaSQL();
        }
        return consultaSQL;
    }
    
    public DynaFormModel getModeloSelectores() {
        return modeloSelectores;
    }

    public DynaFormModel getModeloFiltros() {
        return modeloFiltros;
    }
    
    public DynaFormModel getModeloOrdenadores() {
        return modeloOrdenadores;
    }
    
    public String getFuenteFiltro() {
        return fuenteFiltro;
    }

    public void setFuenteFiltro(String fuenteFiltro) {
        itemsCampoFiltro.clear();
        this.fuenteFiltro = fuenteFiltro;
    }

    public String getCampoFiltro() {
        return campoFiltro;
    }

    public void setCampoFiltro(String campoFiltro) {
        this.campoFiltro = campoFiltro;
    }

    public List<SelectItem> getItemsFuenteFiltro() {
        if( itemsFuenteFiltro.isEmpty() ) {
            for( Fuente fuente : consulta.getFuentes() ) {
                SelectItem item = new SelectItem( fuente.getId(), fuente.getNombre() );
                itemsFuenteFiltro.add( item );
            }
        }
        return itemsFuenteFiltro;
    }

    public List<SelectItem> getItemsCampoFiltro() {
        if( itemsCampoFiltro.isEmpty() && fuenteFiltro != null && !fuenteFiltro.isEmpty() ) {
            Fuente fuente = consulta.getFuentePorId( fuenteFiltro );
            for( Campo campo : fuente.getCampos() ) {
                SelectItem item = new SelectItem( campo.getId(), campo.getNombre() );
                itemsCampoFiltro.add( item );
            }
        }
        return itemsCampoFiltro;
    }

    public String getFuenteOrdenador() {
        return fuenteOrdenador;
    }

    public void setFuenteOrdenador(String fuenteOrdenador) {
        this.fuenteOrdenador = fuenteOrdenador;
    }
    
    public String getCampoOrdenador() {
        return campoOrdenador;
    }

    public void setCampoOrdenador(String campoOrdenador) {
        this.campoOrdenador = campoOrdenador;
    }
    
    public List<SelectItem> getItemsFuenteOrdenador() {
        return itemsFuenteOrdenador;
    }

    public List<SelectItem> getItemsCampoOrdenador() {
        if( itemsCampoOrdenador.isEmpty() ) {
            for( DynaFormControl selector : modeloSelectores.getControls() ) {
                if( selector.getType().equals("selector") &&
                    selector.getData() instanceof CampoFormulario ) {
                    CampoFormulario control = (CampoFormulario)selector.getData();
                    Fuente fuente = consulta.getFuentePorId( control.getId() );
                    String[] valores = (String[])control.getValue();
                    for( String valor : valores ) {
                        Campo campo = consulta.getCampoPorValor(fuente, valor);
                        SelectItem item = new SelectItem( campo.getId(), campo.getNombre() );
                        itemsCampoOrdenador.add(item);
                    }
                }
            }
        }
        return itemsCampoOrdenador;
    }

    public String procesarFlujo( FlowEvent evento ) { 
        if( evento.getOldStep().equals("seleccionTab") ) {
            actualizarSelectores();
            if( consulta.getSelectores().size() > 5 ) {
                FacesContext contexto = FacesContext.getCurrentInstance();
                contexto.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR,
                        "Sólo se pueden elegir hasta 5 datos", "Cantidad excedida"));
                contexto.validationFailed();
                return evento.getOldStep();
            }
        }
        return evento.getNewStep();  
    } 
    
    public String getConfirmacion() {
        actualizar();
        return consulta.crearConsultaLegible();
    }
    
    private void actualizar() {
        
        //Selectores
        actualizarSelectores();
        
        //Filtros
        actualizarFiltros();
        
        //Ordenadores
        actualizarOrdenadores();
        
        modeloFiltros.getRegularRows().clear();
        modeloOrdenadores.getRegularRows().clear();
        fuenteFiltro = null;
        campoFiltro = null;
        fuenteOrdenador = null;
        campoOrdenador = null;
        
    }
    
    private void actualizarSelectores() {
        
        consulta.setSelectores(new ArrayList<Campo>());
        for( DynaFormControl selector : modeloSelectores.getControls() ) {
            if( selector.getType().equals("selector") &&
                selector.getData() instanceof CampoFormulario ) {
                CampoFormulario control = (CampoFormulario)selector.getData();
                Fuente fuente = consulta.getFuentePorId( control.getId() );
                String[] valores = (String[])control.getValue();
                for( String valor : valores ) {
                    Campo campo = consulta.getCampoPorValor(fuente, valor);
                    consulta.getSelectores().add( campo );
                }
            }
        }
        
    }
    
    private void actualizarFiltros() {
        
        consulta.setFiltros(new ArrayList<Filtro>());
        for( DynaFormRow fila : modeloFiltros.getRegularRows() ) {
            List<AbstractDynaFormElement> controles = fila.getElements();
            
            //Campo
            Campo campo = null;
            DynaFormControl controlCampo = (DynaFormControl)controles.get(0);
            if( controlCampo.getType().equals("campo") &&
                controlCampo.getData() instanceof CampoFormulario ) {
                CampoFormulario datosCampo = (CampoFormulario)controlCampo.getData();
                campo = consulta.getCampoPorId( datosCampo.getId() );
            }
            
            //Comparacion
            TipoComparacion comparacion = null;
            DynaFormControl controlComparacion = (DynaFormControl)controles.get(1);
            if( controlComparacion.getType().equals("comparacion") &&
                controlComparacion.getData() instanceof CampoFormulario ) {
                CampoFormulario datosComparacion = (CampoFormulario)controlComparacion.getData();
                comparacion = TipoComparacion.valueOf( datosComparacion.getValue().toString() );
            }
            
            //valor
            String valor = null;
            DynaFormControl controlValor = (DynaFormControl)controles.get(2);
            if( controlValor.getData() instanceof CampoFormulario ) {
                CampoFormulario datosValor = (CampoFormulario)controlValor.getData();
                if( datosValor.getValue() instanceof String ) {
                    valor = datosValor.getValue().toString();
                }
                else if( datosValor.getValue() instanceof Date ) {
                    Date fecha = (Date)datosValor.getValue();
                    SimpleDateFormat formato = new SimpleDateFormat("dd/MM/yyyy");
                    valor = formato.format(fecha);
                }
                else if( datosValor.getValue() instanceof String[] ) {
                    String[] valores = (String[])datosValor.getValue();
                    valor = StringUtils.join(valores, ",");
                }
                
            }
            
            //Comparacion
            boolean requerido = true;
            DynaFormControl controlRequerido = (DynaFormControl)controles.get(3);
            if( controlRequerido.getType().equals("requerido") &&
                controlRequerido.getData() instanceof CampoFormulario ) {
                CampoFormulario datosRequerido = (CampoFormulario)controlRequerido.getData();
                requerido = Boolean.parseBoolean( datosRequerido.getValue().toString() );
            }
            
            consulta.getFiltros().add( new Filtro( campo, comparacion, valor, requerido ) );
            
        }
        
    }
    
    private void actualizarOrdenadores() {
        
        consulta.setOrdenadores(new ArrayList<Ordenador>());
        for( DynaFormRow fila : modeloOrdenadores.getRegularRows() ) {
            List<AbstractDynaFormElement> controles = fila.getElements();
            
            //Campo
            Campo campo = null;
            DynaFormControl controlCampo = (DynaFormControl)controles.get(0);
            if( controlCampo.getType().equals("campo") &&
                controlCampo.getData() instanceof CampoFormulario ) {
                CampoFormulario datosCampo = (CampoFormulario)controlCampo.getData();
                campo = consulta.getCampoPorId( datosCampo.getId() );
            }
            
            //Ordenacion
            TipoOrdenacion ordenacion = null;
            DynaFormControl controlOrdenacion = (DynaFormControl)controles.get(1);
            if( controlOrdenacion.getType().equals("ordenador") &&
                controlOrdenacion.getData() instanceof CampoFormulario ) {
                CampoFormulario datosOrdenacion = (CampoFormulario)controlOrdenacion.getData();
                ordenacion= TipoOrdenacion.valueOf( datosOrdenacion.getValue().toString() );
            }
            
            consulta.getOrdenadores().add( new Ordenador( campo, ordenacion ) );
            
        }
        
    }
    
    private void generarSelectores() {
        for( Fuente fuente : consulta.getFuentes() ) {
            ConstructorFormulario.crearSelector(modeloSelectores, fuente);
        }
    }
    
    public void agregarFiltro( ActionEvent evento ) {
        if( campoFiltro == null || campoFiltro.isEmpty() ) {
            return;
        }
        
        Campo campo = consulta.getCampoPorId( campoFiltro );
        ConstructorFormulario.crearFiltro(modeloFiltros, campo);
        fuenteFiltro = null;
        campoFiltro = null;
    }
    
    public void eliminarFiltro( ActionEvent evento ) {
        String filaId = (String)evento.getComponent().getAttributes().get("fila");
        int hashcode = Integer.parseInt( filaId );
        DynaFormRow filaSeleccionada = null;
        for( DynaFormRow fila : modeloFiltros.getRegularRows() ) {
            if( hashcode == fila.hashCode() ) {
                filaSeleccionada = fila;
                break;
            }
        }
        
        modeloFiltros.getRegularRows().remove(filaSeleccionada);
    }
   
    public void agregarOrdenador( ActionEvent evento ) {
        if( campoOrdenador == null || campoOrdenador.isEmpty() ) {
            return;
        }
        
        Campo campo = consulta.getCampoPorId( campoOrdenador );
        ConstructorFormulario.crearOrdenador(modeloOrdenadores, campo);
        fuenteOrdenador = null;
        campoOrdenador = null;
    }
    
    public void eliminarOrdenador( ActionEvent evento ) {
        String filaId = (String)evento.getComponent().getAttributes().get("fila");
        int hashcode = Integer.parseInt( filaId );
        DynaFormRow filaSeleccionada = null;
        for( DynaFormRow fila : modeloOrdenadores.getRegularRows() ) {
            if( hashcode == fila.hashCode() ) {
                filaSeleccionada = fila;
                break;
            }
        }
        
        modeloOrdenadores.getRegularRows().remove(filaSeleccionada);
    }
    
}

